﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace pwestcott.Util.Tests.Essence
{
    [TestFixture]
    public class CompleteExample
    {
        [EssenceClass(UseIn = EssenceFunctions.All)]
        public class TestEssence : IEquatable<TestEssence>, IComparable<TestEssence>
        {
            [Essence(Order=0, Format="i={0},")]           public int MyInt           { get; set; }
            [Essence(Order=1, Format="s={0},")]           public string MyString     { get; set; }
            [Essence(Order=2, Format="d={0:yyyy-MM-dd}")] public DateTime MyDateTime { get; set; }

            public override int GetHashCode()                                { return Essence<TestEssence>.GetHashCodeStatic(this); }
            public override string ToString()                                { return Essence<TestEssence>.ToStringStatic(this); }
            public int CompareTo(TestEssence other)                          { return Essence<TestEssence>.CompareToStatic(this, other); }

            public static bool operator ==(TestEssence lhs, TestEssence rhs) { return Essence<TestEssence>.EqualsStatic(lhs, rhs); }
            public override bool Equals(object obj)                          { return this == (TestEssence)obj; }
            public bool Equals(TestEssence other)                            { return this == other; }
            public static bool operator !=(TestEssence lhs, TestEssence rhs) { return !(lhs == rhs); }
        }

        public class TestManual
        {
            public int MyInt;
            public string MyString;
            public DateTime MyDateTime;

            public override int GetHashCode()
            {
                var x = MyInt.GetHashCode();
                x *= Essence<TestEssence>.HashCodeMultiplier;
                x ^= (MyString == null) ? 0 : MyString.GetHashCode();
                x *= Essence<TestEssence>.HashCodeMultiplier;
                x ^= MyDateTime.GetHashCode();
                return x;
            }

            public static bool operator ==(TestManual lhs, TestManual rhs)
            {
                if (ReferenceEquals(lhs, null))
                {
                    if (ReferenceEquals(rhs, null))
                        return true;
                    return false;
                }
                if (ReferenceEquals(rhs, null))
                    return false;
                if (ReferenceEquals(lhs, rhs))
                    return true;
                if (typeof(TestManual) != rhs.GetType())
                    return false;

                return lhs.MyInt == rhs.MyInt
                    && lhs.MyString == rhs.MyString
                    && lhs.MyDateTime == rhs.MyDateTime;
            }

            public override bool Equals(object obj)                 { return this == obj as TestManual; }
            public bool Equals(TestManual other)                    { return this == other; }
            public static bool operator !=(TestManual lhs, TestManual rhs) { return !(lhs == rhs); }

            public override string ToString()
            {
                if (MyString == null)
                    return string.Format("i={0},d={1:yyyy-MM-dd}", MyInt, MyDateTime);

                return string.Format("i={0},s={1},d={2:yyyy-MM-dd}", MyInt, MyString, MyDateTime);
            }

            public int CompareTo(TestManual other)
            {
                if (other == null)
                    return 1;
                if (ReferenceEquals(this, other))
                    return 0;

                int result = 0;

                result = MyInt.CompareTo(other.MyInt);
                if (result != 0) return result;
                result = MyString.CompareTo(other.MyString);
                if (result != 0) return result;
                result = MyDateTime.CompareTo(other.MyDateTime);
                if (result != 0) return result;

                return result;
            }
        }

        static readonly int[] ints = { 42, int.MinValue, -1, 93, int.MaxValue };
        static readonly string[] strings = { "Mary", "Mark", "Paul", null, "James" };
        static readonly DateTime[] dates = { new DateTime(1976, 11, 23), DateTime.MaxValue, new DateTime(1973, 11, 13), new DateTime(1999, 4, 18), DateTime.MinValue };

        public void Loop(Action<TestEssence, TestManual> test)
        {
            for (int i = 0; i < ints.Length; ++i)
            {
                var theInt = ints[i];
                for (int j = 0; j < strings.Length; ++j)
                {
                    var theString = strings[j];
                    for (int k = 0; k < dates.Length; ++k)
                    {
                        var theDate = dates[k];

                        TestEssence essence = new TestEssence();
                        TestManual manual = new TestManual(); 

                        essence.MyDateTime = manual.MyDateTime = theDate;
                        essence.MyInt = manual.MyInt = theInt;
                        essence.MyString = manual.MyString = theString;

                        test(essence, manual);
                    }
                }
            }
        }

        void CreateTestObjects(out TestEssence e, out TestManual m)
        {
            e = new TestEssence();
            m = new TestManual();

            e.MyDateTime = m.MyDateTime = dates[0];
            e.MyInt = m.MyInt = ints[0];
            e.MyString = m.MyString = strings[0];
        }

        [Test]
        public void TestGetHashCode()
        {
            Loop((e, m) => Assert.AreEqual(m.GetHashCode(), e.GetHashCode()));
        }

        [Test]
        public void TestEquals()
        {
            TestEssence te;
            TestManual tm;
            CreateTestObjects(out te, out tm);

            Loop((e, m) => Assert.AreEqual(tm.Equals(m), te.Equals(e)));
        }

        [Test]
        public void TestCompareTo()
        {
            TestEssence te;
            TestManual tm;
            CreateTestObjects(out te, out tm);

            Loop((e, m) => Assert.AreEqual(tm.CompareTo(m), te.CompareTo(e)));
        }

        [Test]
        public void TestToString()
        {
            Loop((e, m) => Assert.AreEqual(m.ToString(), e.ToString()));
        }
    }
}
